package sdb

import (
	"os"
	"goaws.googlecode.com/hg/aws"
)

const (
	REGION_US_EAST_ENDPOINT      string = "sdb.amazonaws.com"
	REGION_US_WEST_ENDPOINT      string = "sdb.us-west-1.amazonaws.com"
	REGION_EU_ENDPOINT           string = "sdb.eu-west-1.amazonaws.com"
	REGION_ASIA_PACIFIC_ENDPOINT string = "sdb.ap-southeast-1.amazonaws.com"
)

type Conn struct {
	URL string
	rpc *aws.QueryRPC
}

type Item struct {
	Name       string
	Attributes aws.AttributeList "Attribute" // Tag for xml.Unmarshal
}

type ItemIter struct {
	conn           *Conn
	expr           string
	consistentRead bool
	resp           *selectResponse
}

func Connect(keys aws.AccessKeys, endpoint string, secure bool) *Conn {
	return &Conn{aws.EndpointURL(endpoint, secure) + "/", &aws.QueryRPC{keys, "2009-04-15", "2"}}
}

func (c *Conn) getResponse(action string, params map[string]string, xmlResp interface{}) (err os.Error) {
	return c.rpc.UnmarshalXMLResponse(c.URL, action, params, xmlResp, newErrorResponse)
}

func (it *ItemIter) Item() (item *Item, err os.Error) {
	if len(it.resp.Items) == 0 && it.resp.NextToken != "" {
		resp, err := it.conn.selectNext(it.expr, it.consistentRead, it.resp.NextToken)
		if err != nil {
			return
		}

		it.resp = resp
	}

	if len(it.resp.Items) > 0 {
		item, it.resp.Items = &it.resp.Items[0], it.resp.Items[1:]
		return
	}

	return nil, os.EOF
}

func (c *Conn) SelectIter(expr string, consistentRead bool) (it *ItemIter, err os.Error) {
	resp, err := c.selectNext(expr, consistentRead, "")
	if err != nil {
		return
	}

	return &ItemIter{c, expr, consistentRead, resp}, nil
}

func (c *Conn) Select(expr string, consistentRead bool) (items []Item, err os.Error) {
	resp, err := c.selectNext(expr, consistentRead, "")
	if err != nil {
		return
	}

	items = resp.Items
	for resp.NextToken != "" {
		resp, err = c.selectNext(expr, consistentRead, resp.NextToken)
		if err != nil {
			return nil, err
		}

		items = append(items, resp.Items...)
	}

	return
}

func (c *Conn) selectNext(expr string, consistentRead bool, nextToken string) (resp *selectResponse, err os.Error) {
	params := map[string]string{
		"SelectExpression": expr,
	}
	if consistentRead {
		params["ConsistentRead"] = "true"
	}
	if nextToken != "" {
		params["NextToken"] = nextToken
	}

	resp = new(selectResponse)
	if err = c.getResponse("Select", params, resp); err != nil {
		return nil, err
	}

	return resp, nil
}
